#
# Copyright 2023 The Kumo Ins.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
if (POLICY CMP0042)
    cmake_policy(SET CMP0042 NEW)
endif ()
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/kmcmake/package)
################################################################################################
# options
################################################################################################
option(BUILD_STATIC_LIBRARY "kmcmake set build static library or not" ON)

option(BUILD_SHARED_LIBRARY "kmcmake set build shared library or not" OFF)

option(VERBOSE_KMCMAKE_BUILD "print kmcmake detail information" OFF)

option(VERBOSE_CMAKE_BUILD "verbose cmake make debug" OFF)

option(CONDA_ENV_ENABLE "enable conda auto env" OFF)

option(KMCMAKE_USE_CXX11_ABI "use cxx11 abi or not" ON)

option(KMCMAKE_BUILD_TEST "enable project test or not" OFF)

option(KMCMAKE_BUILD_BENCHMARK "enable project benchmark or not" OFF)

option(KMCMAKE_BUILD_EXAMPLES "enable project examples or not" OFF)

option(KMCMAKE_ENABLE_ARCH "enable cpu instruct" ON)

option(KMCMAKE_ENABLE_CUDA "" OFF)

option(KMCMAKE_STATUS_PRINT "kmcmake print or not, default on" ON)

option(KMCMAKE_INSTALL_LIB "avoid centos install to lib64" OFF)
option(WITH_DEBUG_SYMBOLS "With debug symbols" ON)

################################################################################################
# color
################################################################################################
if (NOT WIN32)
    string(ASCII 27 Esc)
    set(kmcmake_colour_reset "${Esc}[m")
    set(kmcmake_colour_bold "${Esc}[1m")
    set(kmcmake_red "${Esc}[31m")
    set(kmcmake_green "${Esc}[32m")
    set(kmcmake_yellow "${Esc}[33m")
    set(kmcmake_blue "${Esc}[34m")
    set(kmcmake_agenta "${Esc}[35m")
    set(kmcmake_cyan "${Esc}[36m")
    set(kmcmake_white "${Esc}[37m")
    set(kmcmake_bold_red "${Esc}[1;31m")
    set(kmcmake_bold_green "${Esc}[1;32m")
    set(kmcmake_bold_yellow "${Esc}[1;33m")
    set(kmcmake_bold_blue "${Esc}[1;34m")
    set(kmcmake_bold_magenta "${Esc}[1;35m")
    set(kmcmake_bold_cyan "${Esc}[1;36m")
    set(kmcmake_bold_white "${Esc}[1;37m")
endif ()

################################################################################################
# print
################################################################################################
function(kmcmake_debug)
    if (KMCMAKE_STATUS_DEBUG)
        string(TIMESTAMP timestamp)
        if (KMCMAKE_CACHE_RUN)
            set(type "DEBUG (CACHE RUN)")
        else ()
            set(type "DEBUG")
        endif ()
        message(STATUS "${kmcmake_blue}[kmcmake *** ${type} *** ${timestamp}] ${ARGV}${kmcmake_colour_reset}")
    endif ()
endfunction(kmcmake_debug)

function(kmcmake_print)
    if (KMCMAKE_STATUS_PRINT OR KMCMAKE_STATUS_DEBUG)
        if (KMCMAKE_CACHE_RUN)
            kmcmake_debug("${ARGV}")
        else ()
            message(STATUS "${kmcmake_green}[kmcmake] ${ARGV}${kmcmake_colour_reset}")
        endif ()
    endif ()
endfunction(kmcmake_print)

function(kmcmake_error)
    message("")
    foreach (print_message ${ARGV})
        message(SEND_ERROR "${kmcmake_bold_red}[kmcmake ** INTERNAL **] ${print_message}${kmcmake_colour_reset}")
    endforeach ()
    message(FATAL_ERROR "${kmcmake_bold_red}[kmcmake ** INTERNAL **] [Directory:${CMAKE_CURRENT_LIST_DIR}]${kmcmake_colour_reset}")
    message("")
endfunction(kmcmake_error)

function(kmcmake_warn)
    message("")
    foreach (print_message ${ARGV})
        message(WARNING "${kmcmake_red}[kmcmake WARNING] ${print_message}${kmcmake_colour_reset}")
    endforeach ()
    message(WARNING "${kmcmake_red}[kmcmake WARNING] [Directory:${CMAKE_CURRENT_LIST_DIR}]${kmcmake_colour_reset}")
    message("")
endfunction(kmcmake_warn)

set(KMCMAKE_ALIGN_LENGTH 30)
MACRO(kmcmake_print_label Label Value)
    string(LENGTH ${Label} lLength)
    math(EXPR paddingLeng ${KMCMAKE_ALIGN_LENGTH}-${lLength})
    string(REPEAT " " ${paddingLeng} PADDING)
    message("${kmcmake_yellow}${Label}${kmcmake_colour_reset}:${PADDING}${kmcmake_cyan}${Value}${kmcmake_colour_reset}")
ENDMACRO()

MACRO(kmcmake_raw Value)
    message("${Value}")
ENDMACRO()

MACRO(kmcmake_directory_list result curdir)
    FILE(GLOB children RELATIVE ${curdir} ${curdir}/*)
    SET(dirlist "")
    FOREACH (child ${children})
        IF (IS_DIRECTORY ${curdir}/${child})
            LIST(APPEND dirlist ${child})
        ENDIF ()
    ENDFOREACH ()
    SET(${result} ${dirlist})
ENDMACRO()

MACRO(kmcmake_print_list result)
    foreach (arg IN LISTS ${result})
        message(" - ${kmcmake_cyan}${arg}${kmcmake_colour_reset}")
    endforeach ()
ENDMACRO()


MACRO(kmcmake_print_list_label Label ListVar)
    message("${kmcmake_yellow}${Label}${kmcmake_colour_reset}:")
    kmcmake_print_list(${ListVar})
ENDMACRO()

################################################################################################
# platform info
################################################################################################
cmake_host_system_information(RESULT KMCMAKE_PRETTY_NAME QUERY DISTRIB_PRETTY_NAME)
string(TOLOWER ${KMCMAKE_PRETTY_NAME} LC_KMCMAKE_PRETTY_NAME)
string(TOUPPER ${KMCMAKE_PRETTY_NAME} UP_KMCMAKE_PRETTY_NAME)
kmcmake_print("${KMCMAKE_PRETTY_NAME}")

cmake_host_system_information(RESULT KMCMAKE_DISTRO QUERY DISTRIB_INFO)
kmcmake_print_list_label("KMCMAKE_DISTRO:" KMCMAKE_DISTRO)
foreach (dis IN LISTS KMCMAKE_DISTRO)
    kmcmake_print("${dis} = `${${dis}}`")
endforeach ()


################################################################################################
# install dir
################################################################################################
include(GNUInstallDirs)

if (${PROJECT_NAME}_VERSION)
    set(KMCMAKE_SUBDIR "${PROJECT_NAME}_${PROJECT_VERSION}")
    set(KMCMAKE_INSTALL_BINDIR "${CMAKE_INSTALL_BINDIR}/${KMCMAKE_SUBDIR}")
    set(KMCMAKE_INSTALL_CONFIGDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${KMCMAKE_SUBDIR}")
    set(KMCMAKE_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}/{KMCMAKE_SUBDIR}")
    set(KMCMAKE_INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}/${KMCMAKE_SUBDIR}")
else ()
    set(KMCMAKE_INSTALL_BINDIR "${CMAKE_INSTALL_BINDIR}")
    set(KMCMAKE_INSTALL_CONFIGDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
    set(KMCMAKE_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}")
    set(KMCMAKE_INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}")
endif ()

################################################################################################
# kmcmake_cc_library
################################################################################################

################################################################################
# Create a Library.
#
# Example usage:
#
# kmcmake_cc_library(  NAME myLibrary
#                  NAMESPACE myNamespace
#                  SOURCES
#                       myLib.cpp
#                       myLib_functions.cpp
#                  DEFINES
#                     USE_DOUBLE_PRECISION=1
#                  PUBLIC_INCLUDE_PATHS
#                     ${CMAKE_SOURCE_DIR}/mylib/include
#                  PRIVATE_INCLUDE_PATHS
#                     ${CMAKE_SOURCE_DIR}/include
#                  PRIVATE_LINKED_TARGETS
#                     Threads::Threads
#                  PUBLIC_LINKED_TARGETS
#                     Threads::Threads
#                  LINKED_TARGETS
#                     Threads::Threads
# )
#
# The above example creates an alias target, myNamespace::myLibrary which can be
# linked to by other tar gets.
# PUBLIC_DEFINES -  preprocessor defines which are inherated by targets which
#                       link to this library
#
#
# PUBLIC_INCLUDE_PATHS - include paths which are public, therefore inherted by
#                        targest which link to this library.
#
# PRIVATE_INCLUDE_PATHS - private include paths which are only visible by MyLibrary
#
# LINKED_TARGETS        - targets to link to.
################################################################################
function(kmcmake_cc_library)
    set(options
            PUBLIC
            EXCLUDE_SYSTEM
    )
    set(args NAME
            NAMESPACE
    )

    set(list_args
            DEPS
            SOURCES
            OBJECTS
            HEADERS
            INCLUDES
            PINCLUDES
            DEFINES
            COPTS
            CXXOPTS
            CUOPTS
            LINKS
            PLINKS
            WLINKS
    )

    cmake_parse_arguments(
            PARSE_ARGV 0
            KMCMAKE_CC_LIB
            "${options}"
            "${args}"
            "${list_args}"
    )

    if ("${KMCMAKE_CC_LIB_NAME}" STREQUAL "")
        get_filename_component(KMCMAKE_CC_LIB_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME)
        string(REPLACE " " "_" KMCMAKE_CC_LIB_NAME ${KMCMAKE_CC_LIB_NAME})
        kmcmake_print(" Library, NAME argument not provided. Using folder name:  ${KMCMAKE_CC_LIB_NAME}")
    endif ()

    if ("${KMCMAKE_CC_LIB_NAMESPACE}" STREQUAL "")
        set(KMCMAKE_CC_LIB_NAMESPACE ${PROJECT_NAME})
        kmcmake_print(" Library, NAMESPACE argument not provided. Using target alias:  ${KMCMAKE_CC_LIB_NAME}::${KMCMAKE_CC_LIB_NAME}")
    endif ()

    kmcmake_raw("-----------------------------------")
    if (KMCMAKE_CC_LIB_PUBLIC)
        set(KMCMAKE_LIB_INFO "${KMCMAKE_CC_LIB_NAMESPACE}::${KMCMAKE_CC_LIB_NAME}  SHARED&STATIC PUBLIC")
    else ()
        set(KMCMAKE_LIB_INFO "${KMCMAKE_CC_LIB_NAMESPACE}::${KMCMAKE_CC_LIB_NAME}  SHARED&STATIC INTERNAL")
    endif ()

    set(${KMCMAKE_CC_LIB_NAME}_INCLUDE_SYSTEM SYSTEM)
    if (KMCMAKE_CC_LIB_EXCLUDE_SYSTEM)
        set(${KMCMAKE_CC_LIB_NAME}_INCLUDE_SYSTEM "")
    endif ()

    kmcmake_print_label("Create Library" "${KMCMAKE_LIB_INFO}")
    kmcmake_raw("-----------------------------------")
    if (VERBOSE_KMCMAKE_BUILD)
        kmcmake_print_list_label("Sources" KMCMAKE_CC_LIB_SOURCES)
        kmcmake_print_list_label("Objects" KMCMAKE_CC_LIB_OBJECTS)
        kmcmake_print_list_label("Deps" KMCMAKE_CC_LIB_DEPS)
        kmcmake_print_list_label("COPTS" KMCMAKE_CC_LIB_COPTS)
        kmcmake_print_list_label("CXXOPTS" KMCMAKE_CC_LIB_CXXOPTS)
        kmcmake_print_list_label("CUOPTS" KMCMAKE_CC_LIB_CUOPTS)
        kmcmake_print_list_label("Defines" KMCMAKE_CC_LIB_DEFINES)
        kmcmake_print_list_label("Includes" KMCMAKE_CC_LIB_INCLUDES)
        kmcmake_print_list_label("Private Includes" KMCMAKE_CC_LIB_PINCLUDES)
        kmcmake_print_list_label("Links" KMCMAKE_CC_LIB_LINKS)
        kmcmake_print_list_label("Private Links" KMCMAKE_CC_LIB_PLINKS)
        kmcmake_raw("-----------------------------------")
    endif ()
    set(KMCMAKE_CC_LIB_OBJECTS_FLATTEN)
    if (KMCMAKE_CC_LIB_OBJECTS)
        foreach (obj IN LISTS KMCMAKE_CC_LIB_OBJECTS)
            list(APPEND KMCMAKE_CC_LIB_OBJECTS_FLATTEN $<TARGET_OBJECTS:${obj}>)
        endforeach ()
    endif ()
    if (KMCMAKE_CC_LIB_SOURCES)
        add_library(${KMCMAKE_CC_LIB_NAME}_OBJECT OBJECT ${KMCMAKE_CC_LIB_SOURCES} ${KMCMAKE_CC_LIB_HEADERS})
        list(APPEND KMCMAKE_CC_LIB_OBJECTS_FLATTEN $<TARGET_OBJECTS:${KMCMAKE_CC_LIB_NAME}_OBJECT>)
        if (KMCMAKE_CC_LIB_DEPS)
            add_dependencies(${KMCMAKE_CC_LIB_NAME}_OBJECT ${KMCMAKE_CC_LIB_DEPS})
        endif ()
        set_property(TARGET ${KMCMAKE_CC_LIB_NAME}_OBJECT PROPERTY POSITION_INDEPENDENT_CODE 1)
        target_compile_options(${KMCMAKE_CC_LIB_NAME}_OBJECT PRIVATE $<$<COMPILE_LANGUAGE:C>:${KMCMAKE_CC_LIB_COPTS}>)
        target_compile_options(${KMCMAKE_CC_LIB_NAME}_OBJECT PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${KMCMAKE_CC_LIB_CXXOPTS}>)
        target_compile_options(${KMCMAKE_CC_LIB_NAME}_OBJECT PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:${KMCMAKE_CC_LIB_CUOPTS}>)
        target_include_directories(${KMCMAKE_CC_LIB_NAME}_OBJECT ${${KMCMAKE_CC_LIB_NAME}_INCLUDE_SYSTEM}
                PUBLIC
                ${KMCMAKE_CC_LIB_INCLUDES}
                "$<BUILD_INTERFACE:${${PROJECT_NAME}_SOURCE_DIR}>"
                "$<BUILD_INTERFACE:${${PROJECT_NAME}_BINARY_DIR}>"
                "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
        )
        target_include_directories(${KMCMAKE_CC_LIB_NAME}_OBJECT ${${KMCMAKE_CC_LIB_NAME}_INCLUDE_SYSTEM}
                PRIVATE
                ${KMCMAKE_CC_LIB_PINCLUDES}
        )

        target_compile_definitions(${KMCMAKE_CC_LIB_NAME}_OBJECT
                PUBLIC
                ${KMCMAKE_CC_LIB_DEFINES}
        )
    endif ()

    list(LENGTH KMCMAKE_CC_LIB_OBJECTS_FLATTEN obj_len)
    if (obj_len EQUAL -1)
        kmcmake_error("no source or object give to the library ${KMCMAKE_CC_LIB_NAME}")
    endif ()
    add_library(${KMCMAKE_CC_LIB_NAME}_static STATIC ${KMCMAKE_CC_LIB_OBJECTS_FLATTEN})
    if (${KMCMAKE_CC_LIB_NAME}_OBJECT)
        add_dependencies(${KMCMAKE_CC_LIB_NAME}_static ${KMCMAKE_CC_LIB_NAME}_OBJECT)
    endif ()
    if (KMCMAKE_CC_LIB_DEPS)
        add_dependencies(${KMCMAKE_CC_LIB_NAME}_static ${KMCMAKE_CC_LIB_DEPS})
    endif ()
    target_link_libraries(${KMCMAKE_CC_LIB_NAME}_static PRIVATE ${KMCMAKE_CC_LIB_PLINKS})
    target_link_libraries(${KMCMAKE_CC_LIB_NAME}_static PUBLIC ${KMCMAKE_CC_LIB_LINKS})
    target_link_libraries(${KMCMAKE_CC_LIB_NAME}_static PRIVATE ${KMCMAKE_CC_LIB_WLINKS})
    set_target_properties(${KMCMAKE_CC_LIB_NAME}_static PROPERTIES
            OUTPUT_NAME ${KMCMAKE_CC_LIB_NAME})
    add_library(${KMCMAKE_CC_LIB_NAMESPACE}::${KMCMAKE_CC_LIB_NAME}_static ALIAS ${KMCMAKE_CC_LIB_NAME}_static)

    add_library(${KMCMAKE_CC_LIB_NAME}_shared SHARED ${KMCMAKE_CC_LIB_OBJECTS_FLATTEN})
    if (${KMCMAKE_CC_LIB_NAME}_OBJECT)
        add_dependencies(${KMCMAKE_CC_LIB_NAME}_shared ${KMCMAKE_CC_LIB_NAME}_OBJECT)
    endif ()
    if (KMCMAKE_CC_LIB_DEPS)
        add_dependencies(${KMCMAKE_CC_LIB_NAME}_shared ${KMCMAKE_CC_LIB_DEPS})
    endif ()
    target_link_libraries(${KMCMAKE_CC_LIB_NAME}_shared PRIVATE ${KMCMAKE_CC_LIB_PLINKS})
    target_link_libraries(${KMCMAKE_CC_LIB_NAME}_shared PUBLIC ${KMCMAKE_CC_LIB_LINKS})
    foreach (link ${KMCMAKE_CC_LIB_WLINKS})
        target_link_libraries(${KMCMAKE_CC_LIB_NAME}_shared PRIVATE $<LINK_LIBRARY:WHOLE_ARCHIVE,${link}>)
    endforeach ()
    set_target_properties(${KMCMAKE_CC_LIB_NAME}_shared PROPERTIES
            OUTPUT_NAME ${KMCMAKE_CC_LIB_NAME}
            VERSION ${${PROJECT_NAME}_VERSION}
            SOVERSION ${${PROJECT_NAME}_VERSION_MAJOR})
    add_library(${KMCMAKE_CC_LIB_NAMESPACE}::${KMCMAKE_CC_LIB_NAME} ALIAS ${KMCMAKE_CC_LIB_NAME}_shared)

    if (KMCMAKE_CC_LIB_PUBLIC)
        install(TARGETS ${KMCMAKE_CC_LIB_NAME}_shared ${KMCMAKE_CC_LIB_NAME}_static
                EXPORT ${PROJECT_NAME}Targets
                RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
                LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
                ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
                INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
        )
    endif ()

    foreach (arg IN LISTS KMCMAKE_CC_LIB_UNPARSED_ARGUMENTS)
        message(WARNING "Unparsed argument: ${arg}")
    endforeach ()

endfunction()

function(kmcmake_cc_test_library)
    set(options
            SHARED
            EXCLUDE_SYSTEM
    )
    set(args NAME
            NAMESPACE
    )

    set(list_args
            DEPS
            SOURCES
            OBJECTS
            HEADERS
            INCLUDES
            PINCLUDES
            DEFINES
            COPTS
            CXXOPTS
            CUOPTS
            LINKS
            PLINKS
            WLINKS
    )

    cmake_parse_arguments(
            PARSE_ARGV 0
            KMCMAKE_CC_LIB
            "${options}"
            "${args}"
            "${list_args}"
    )

    if ("${KMCMAKE_CC_LIB_NAME}" STREQUAL "")
        get_filename_component(KMCMAKE_CC_LIB_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME)
        string(REPLACE " " "_" KMCMAKE_CC_LIB_NAME ${KMCMAKE_CC_LIB_NAME})
        kmcmake_print(" Library, NAME argument not provided. Using folder name:  ${KMCMAKE_CC_LIB_NAME}")
    endif ()

    if ("${KMCMAKE_CC_LIB_NAMESPACE}" STREQUAL "")
        set(KMCMAKE_CC_LIB_NAMESPACE ${PROJECT_NAME})
        kmcmake_print(" Library, NAMESPACE argument not provided. Using target alias:  ${KMCMAKE_CC_LIB_NAME}::${KMCMAKE_CC_LIB_NAME}")
    endif ()

    kmcmake_raw("-----------------------------------")
    if (KMCMAKE_CC_LIB_SHARED)
        set(KMCMAKE_LIB_INFO "${KMCMAKE_CC_LIB_NAMESPACE}::${KMCMAKE_CC_LIB_NAME}  SHARED&STATIC INTERNAL")
    else ()
        set(KMCMAKE_LIB_INFO "${KMCMAKE_CC_LIB_NAMESPACE}::${KMCMAKE_CC_LIB_NAME}  STATIC INTERNAL")
    endif ()

    set(${KMCMAKE_CC_LIB_NAME}_INCLUDE_SYSTEM SYSTEM)
    if (KMCMAKE_CC_LIB_EXCLUDE_SYSTEM)
        set(${KMCMAKE_CC_LIB_NAME}_INCLUDE_SYSTEM "")
    endif ()

    kmcmake_print_label("Create Library" "${KMCMAKE_LIB_INFO}")
    kmcmake_raw("-----------------------------------")
    if (VERBOSE_KMCMAKE_BUILD)
        kmcmake_print_list_label("Sources" KMCMAKE_CC_LIB_SOURCES)
        kmcmake_print_list_label("Objects" KMCMAKE_CC_LIB_OBJECTS)
        kmcmake_print_list_label("Deps" KMCMAKE_CC_LIB_DEPS)
        kmcmake_print_list_label("COPTS" KMCMAKE_CC_LIB_COPTS)
        kmcmake_print_list_label("CXXOPTS" KMCMAKE_CC_LIB_CXXOPTS)
        kmcmake_print_list_label("CUOPTS" KMCMAKE_CC_LIB_CUOPTS)
        kmcmake_print_list_label("Defines" KMCMAKE_CC_LIB_DEFINES)
        kmcmake_print_list_label("Includes" KMCMAKE_CC_LIB_INCLUDES)
        kmcmake_print_list_label("Private Includes" KMCMAKE_CC_LIB_PINCLUDES)
        kmcmake_print_list_label("Links" KMCMAKE_CC_LIB_LINKS)
        kmcmake_print_list_label("Private Links" KMCMAKE_CC_LIB_PLINKS)
        kmcmake_raw("-----------------------------------")
    endif ()
    set(KMCMAKE_CC_LIB_OBJECTS_FLATTEN)
    if (KMCMAKE_CC_LIB_OBJECTS)
        foreach (obj IN LISTS KMCMAKE_CC_LIB_OBJECTS)
            list(APPEND KMCMAKE_CC_LIB_OBJECTS_FLATTEN $<TARGET_OBJECTS:${obj}>)
        endforeach ()
    endif ()
    if (KMCMAKE_CC_LIB_SOURCES)
        add_library(${KMCMAKE_CC_LIB_NAME}_OBJECT OBJECT ${KMCMAKE_CC_LIB_SOURCES} ${KMCMAKE_CC_LIB_HEADERS})
        list(APPEND KMCMAKE_CC_LIB_OBJECTS_FLATTEN $<TARGET_OBJECTS:${KMCMAKE_CC_LIB_NAME}_OBJECT>)
        if (KMCMAKE_CC_LIB_DEPS)
            add_dependencies(${KMCMAKE_CC_LIB_NAME}_OBJECT ${KMCMAKE_CC_LIB_DEPS})
        endif ()
        set_property(TARGET ${KMCMAKE_CC_LIB_NAME}_OBJECT PROPERTY POSITION_INDEPENDENT_CODE 1)
        target_compile_options(${KMCMAKE_CC_LIB_NAME}_OBJECT PRIVATE $<$<COMPILE_LANGUAGE:C>:${KMCMAKE_CC_LIB_COPTS}>)
        target_compile_options(${KMCMAKE_CC_LIB_NAME}_OBJECT PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${KMCMAKE_CC_LIB_CXXOPTS}>)
        target_compile_options(${KMCMAKE_CC_LIB_NAME}_OBJECT PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:${KMCMAKE_CC_LIB_CUOPTS}>)
        target_include_directories(${KMCMAKE_CC_LIB_NAME}_OBJECT ${${KMCMAKE_CC_LIB_NAME}_INCLUDE_SYSTEM}
                PUBLIC
                ${KMCMAKE_CC_LIB_INCLUDES}
                "$<BUILD_INTERFACE:${${PROJECT_NAME}_SOURCE_DIR}>"
                "$<BUILD_INTERFACE:${${PROJECT_NAME}_BINARY_DIR}>"
                "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
        )
        target_include_directories(${KMCMAKE_CC_LIB_NAME}_OBJECT ${${KMCMAKE_CC_LIB_NAME}_INCLUDE_SYSTEM}
                PRIVATE
                ${KMCMAKE_CC_LIB_PINCLUDES}
        )

        target_compile_definitions(${KMCMAKE_CC_LIB_NAME}_OBJECT
                PUBLIC
                ${KMCMAKE_CC_LIB_DEFINES}
        )
    endif ()

    list(LENGTH KMCMAKE_CC_LIB_OBJECTS_FLATTEN obj_len)
    if (obj_len EQUAL -1)
        kmcmake_error("no source or object give to the library ${KMCMAKE_CC_LIB_NAME}")
    endif ()
    add_library(${KMCMAKE_CC_LIB_NAME}_static STATIC ${KMCMAKE_CC_LIB_OBJECTS_FLATTEN})
    if (${KMCMAKE_CC_LIB_NAME}_OBJECT)
        add_dependencies(${KMCMAKE_CC_LIB_NAME}_static ${KMCMAKE_CC_LIB_NAME}_OBJECT)
    endif ()
    if (KMCMAKE_CC_LIB_DEPS)
        add_dependencies(${KMCMAKE_CC_LIB_NAME}_static ${KMCMAKE_CC_LIB_DEPS})
    endif ()
    target_link_libraries(${KMCMAKE_CC_LIB_NAME}_static PRIVATE ${KMCMAKE_CC_LIB_PLINKS})
    target_link_libraries(${KMCMAKE_CC_LIB_NAME}_static PUBLIC ${KMCMAKE_CC_LIB_LINKS})
    target_link_libraries(${KMCMAKE_CC_LIB_NAME}_static PRIVATE ${KMCMAKE_CC_LIB_WLINKS})
    set_target_properties(${KMCMAKE_CC_LIB_NAME}_static PROPERTIES
            OUTPUT_NAME ${KMCMAKE_CC_LIB_NAME})
    add_library(${KMCMAKE_CC_LIB_NAMESPACE}::${KMCMAKE_CC_LIB_NAME}_static ALIAS ${KMCMAKE_CC_LIB_NAME}_static)
    if (KMCMAKE_CC_LIB_SHARED)
        add_library(${KMCMAKE_CC_LIB_NAME}_shared SHARED ${KMCMAKE_CC_LIB_OBJECTS_FLATTEN})
        if (${KMCMAKE_CC_LIB_NAME}_OBJECT)
            add_dependencies(${KMCMAKE_CC_LIB_NAME}_shared ${KMCMAKE_CC_LIB_NAME}_OBJECT)
        endif ()
        if (KMCMAKE_CC_LIB_DEPS)
            add_dependencies(${KMCMAKE_CC_LIB_NAME}_shared ${KMCMAKE_CC_LIB_DEPS})
        endif ()
        target_link_libraries(${KMCMAKE_CC_LIB_NAME}_shared PRIVATE ${KMCMAKE_CC_LIB_PLINKS})
        target_link_libraries(${KMCMAKE_CC_LIB_NAME}_shared PUBLIC ${KMCMAKE_CC_LIB_LINKS})
        foreach (link ${KMCMAKE_CC_LIB_WLINKS})
            target_link_libraries(${KMCMAKE_CC_LIB_NAME}_shared PRIVATE $<LINK_LIBRARY:WHOLE_ARCHIVE,${link}>)
        endforeach ()
        set_target_properties(${KMCMAKE_CC_LIB_NAME}_shared PROPERTIES
                OUTPUT_NAME ${KMCMAKE_CC_LIB_NAME}
                VERSION ${${PROJECT_NAME}_VERSION}
                SOVERSION ${${PROJECT_NAME}_VERSION_MAJOR})
        add_library(${KMCMAKE_CC_LIB_NAMESPACE}::${KMCMAKE_CC_LIB_NAME} ALIAS ${KMCMAKE_CC_LIB_NAME}_shared)
    endif ()

    foreach (arg IN LISTS KMCMAKE_CC_LIB_UNPARSED_ARGUMENTS)
        message(WARNING "Unparsed argument: ${arg}")
    endforeach ()

endfunction()

################################################################################################
# kmcmake_cc_interface
################################################################################################
################################################################################
# Create a Library.
#
# Example usage:
#
# kmcmake_cc_library(  NAME myLibrary
#                  NAMESPACE myNamespace
#                  SOURCES
#                       myLib.cpp
#                       myLib_functions.cpp
#                  DEFINES
#                     USE_DOUBLE_PRECISION=1
#                  PUBLIC_INCLUDE_PATHS
#                     ${CMAKE_SOURCE_DIR}/mylib/include
#                  PRIVATE_INCLUDE_PATHS
#                     ${CMAKE_SOURCE_DIR}/include
#                  PRIVATE_LINKED_TARGETS
#                     Threads::Threads
#                  PUBLIC_LINKED_TARGETS
#                     Threads::Threads
#                  LINKED_TARGETS
#                     Threads::Threads
# )
#
# The above example creates an alias target, myNamespace::myLibrary which can be
# linked to by other tar gets.
# PUBLIC_DEFINES -  preprocessor defines which are inherated by targets which
#                       link to this library
#
#
# PUBLIC_INCLUDE_PATHS - include paths which are public, therefore inherted by
#                        targest which link to this library.
#
# PRIVATE_INCLUDE_PATHS - private include paths which are only visible by MyLibrary
#
# LINKED_TARGETS        - targets to link to.
################################################################################
function(kmcmake_cc_interface)
    set(options
            PUBLIC
            EXCLUDE_SYSTEM
    )
    set(args NAME
            NAMESPACE
    )

    set(list_args
            HEADERS
            COPTS
            CXXOPTS
            CUOPTS
            DEFINES
    )

    cmake_parse_arguments(
            PARSE_ARGV 0
            KMCMAKE_CC_INTERFACE
            "${options}"
            "${args}"
            "${list_args}"
    )

    if ("${KMCMAKE_CC_INTERFACE_NAME}" STREQUAL "")
        get_filename_component(KMCMAKE_CC_INTERFACE_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME)
        string(REPLACE " " "_" KMCMAKE_CC_INTERFACE_NAME ${KMCMAKE_CC_INTERFACE_NAME})
        kmcmake_print(" Library, NAME argument not provided. Using folder name:  ${KMCMAKE_CC_INTERFACE_NAME}")
    endif ()

    if ("${KMCMAKE_CC_INTERFACE_NAMESPACE}" STREQUAL "")
        set(KMCMAKE_CC_INTERFACE_NAMESPACE ${PROJECT_NAME})
        message(" Library, NAMESPACE argument not provided. Using target alias:  ${KMCMAKE_CC_INTERFACE_NAME}::${KMCMAKE_CC_INTERFACE_NAME}")
    endif ()

    if ("${KMCMAKE_CC_INTERFACE_HEADERS}" STREQUAL "")
        kmcmake_error("no source give to the interface ${KMCMAKE_CC_INTERFACE_NAME}")
    endif ()

    set(${KMCMAKE_CC_INTERFACE_NAME}_INCLUDE_SYSTEM SYSTEM)
    if (KMCMAKE_CC_INTERFACE_EXCLUDE_SYSTEM)
        set(${KMCMAKE_CC_INTERFACE_NAME}_INCLUDE_SYSTEM "")
    endif ()

    kmcmake_raw("-----------------------------------")
    if (KMCMAKE_CC_INTERFACE_PUBLIC)
        set(KMCMAKE_CC_INTERFACE_INFO "${KMCMAKE_CC_INTERFACE_NAMESPACE}::${KMCMAKE_CC_INTERFACE_NAME}  INTERFACE PUBLIC")
    else ()
        set(KMCMAKE_CC_INTERFACE_INFO "${KMCMAKE_CC_INTERFACE_NAMESPACE}::${KMCMAKE_CC_INTERFACE_NAME}  INTERFACE INTERNAL")
    endif ()
    kmcmake_print_label("Create Library" "${KMCMAKE_CC_INTERFACE_INFO}")
    kmcmake_raw("-----------------------------------")
    if (VERBOSE_KMCMAKE_BUILD)
        kmcmake_print_list_label("Headers" KMCMAKE_CC_INTERFACE_HEADERS)
        kmcmake_raw("-----------------------------------")
    endif ()

    add_library(${KMCMAKE_CC_INTERFACE_NAME} INTERFACE)
    add_library(${KMCMAKE_CC_INTERFACE_NAMESPACE}::${KMCMAKE_CC_INTERFACE_NAME} ALIAS ${KMCMAKE_CC_INTERFACE_NAME})

    target_compile_options(${KMCMAKE_CC_INTERFACE_NAME} INTERFACE $<$<COMPILE_LANGUAGE:C>:${KMCMAKE_CC_INTERFACE_COPTS}>)
    target_compile_options(${KMCMAKE_CC_INTERFACE_NAME} INTERFACE $<$<COMPILE_LANGUAGE:CXX>:${KMCMAKE_CC_INTERFACE_CXXOPTS}>)
    target_compile_options(${KMCMAKE_CC_INTERFACE_NAME} INTERFACE $<$<COMPILE_LANGUAGE:CUDA>:${KMCMAKE_CC_INTERFACE_CUOPTS}>)

    target_include_directories(${KMCMAKE_CC_INTERFACE_NAME} ${${KMCMAKE_CC_INTERFACE_NAME}_INCLUDE_SYSTEM}
            INTERFACE
            "$<BUILD_INTERFACE:${${PROJECT_NAME}_SOURCE_DIR}>"
            "$<BUILD_INTERFACE:${${PROJECT_NAME}_BINARY_DIR}>"
            "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
    )

    target_compile_definitions(${KMCMAKE_CC_INTERFACE_NAME} INTERFACE ${KMCMAKE_CC_INTERFACE_DEFINES})


    if (KMCMAKE_CC_INTERFACE_PUBLIC)
        install(TARGETS ${KMCMAKE_CC_INTERFACE_NAME}
                EXPORT ${PROJECT_NAME}Targets
                RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
                LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
                ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
                INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
        )
    endif ()

    foreach (arg IN LISTS KMCMAKE_CC_INTERFACE_UNPARSED_ARGUMENTS)
        message(WARNING "Unparsed argument: ${arg}")
    endforeach ()

endfunction()

################################################################################################
# kmcmake_cc_object
################################################################################################
################################################################################
# Create a Library.
#
# Example usage:
#
# kmcmake_cc_object(  NAME myLibrary
#                  NAMESPACE myNamespace
#                  SOURCES
#                       myLib.cpp
#                       myLib_functions.cpp
#                  DEFINES
#                     USE_DOUBLE_PRECISION=1
#                  PUBLIC_INCLUDE_PATHS
#                     ${CMAKE_SOURCE_DIR}/mylib/include
#                  PRIVATE_INCLUDE_PATHS
#                     ${CMAKE_SOURCE_DIR}/include
#                  PRIVATE_LINKED_TARGETS
#                     Threads::Threads
#                  PUBLIC_LINKED_TARGETS
#                     Threads::Threads
#                  LINKED_TARGETS
#                     Threads::Threads
# )
#
# The above example creates an alias target, myNamespace::myLibrary which can be
# linked to by other tar gets.
# PUBLIC_DEFINES -  preprocessor defines which are inherated by targets which
#                       link to this library
#
#
# PUBLIC_INCLUDE_PATHS - include paths which are public, therefore inherted by
#                        targest which link to this library.
#
# PRIVATE_INCLUDE_PATHS - private include paths which are only visible by MyLibrary
#
# LINKED_TARGETS        - targets to link to.
################################################################################
function(kmcmake_cc_object)
    set(options
            EXCLUDE_SYSTEM
    )
    set(args NAME
            NAMESPACE
    )

    set(list_args
            DEPS
            SOURCES
            HEADERS
            INCLUDES
            PINCLUDES
            DEFINES
            COPTS
            CXXOPTS
            CUOPTS
    )

    cmake_parse_arguments(
            PARSE_ARGV 0
            KMCMAKE_CC_OBJECT
            "${options}"
            "${args}"
            "${list_args}"
    )

    if ("${KMCMAKE_CC_OBJECT_NAME}" STREQUAL "")
        get_filename_component(KMCMAKE_CC_OBJECT_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME)
        string(REPLACE " " "_" KMCMAKE_CC_OBJECT_NAME ${KMCMAKE_CC_OBJECT_NAME})
        kmcmake_print(" Library, NAME argument not provided. Using folder name:  ${KMCMAKE_CC_OBJECT_NAME}")
    endif ()

    if (NOT KMCMAKE_CC_OBJECT_NAMESPACE OR "${KMCMAKE_CC_OBJECT_NAMESPACE}" STREQUAL "")
        set(KMCMAKE_CC_OBJECT_NAMESPACE ${PROJECT_NAME})
        kmcmake_print(" Library, NAMESPACE argument not provided. Using target alias:  ${KMCMAKE_CC_OBJECT_NAME}::${KMCMAKE_CC_OBJECT_NAME}")
    endif ()

    if ("${KMCMAKE_CC_OBJECT_SOURCES}" STREQUAL "")
        kmcmake_error("no source give to the library ${KMCMAKE_CC_OBJECT_NAME}, using kmcmake_cc_object instead")
    endif ()

    kmcmake_raw("-----------------------------------")
    set(KMCMAKE_LIB_INFO "${KMCMAKE_CC_OBJECT_NAMESPACE}::${KMCMAKE_CC_OBJECT_NAME}  OBJECT ")

    set(${KMCMAKE_CC_OBJECT_NAME}_INCLUDE_SYSTEM SYSTEM)
    if (KMCMAKE_CC_OBJECT_EXCLUDE_SYSTEM)
        set(${KMCMAKE_CC_OBJECT_NAME}_INCLUDE_SYSTEM "")
    endif ()

    kmcmake_print_label("Create Library" "${KMCMAKE_LIB_INFO}")
    kmcmake_raw("-----------------------------------")
    if (VERBOSE_KMCMAKE_BUILD)
        kmcmake_print_list_label("Sources" KMCMAKE_CC_OBJECT_SOURCES)
        kmcmake_print_list_label("Deps" KMCMAKE_CC_OBJECT_DEPS)
        kmcmake_print_list_label("COPTS" KMCMAKE_CC_OBJECT_COPTS)
        kmcmake_print_list_label("CXXOPTS" KMCMAKE_CC_OBJECT_CXXOPTS)
        kmcmake_print_list_label("CUOPTS" KMCMAKE_CC_OBJECT_CUOPTS)
        kmcmake_print_list_label("Defines" KMCMAKE_CC_OBJECT_DEFINES)
        kmcmake_print_list_label("Includes" KMCMAKE_CC_OBJECT_INCLUDES)
        kmcmake_print_list_label("Private Includes" KMCMAKE_CC_OBJECT_PINCLUDES)
        kmcmake_raw("-----------------------------------")
    endif ()
    add_library(${KMCMAKE_CC_OBJECT_NAME} OBJECT ${KMCMAKE_CC_OBJECT_SOURCES} ${KMCMAKE_CC_OBJECT_HEADERS})
    if (KMCMAKE_CC_OBJECT_DEPS)
        add_dependencies(${KMCMAKE_CC_OBJECT_NAME} ${KMCMAKE_CC_OBJECT_DEPS})
    endif ()
    set_property(TARGET ${KMCMAKE_CC_OBJECT_NAME} PROPERTY POSITION_INDEPENDENT_CODE 1)
    target_compile_options(${KMCMAKE_CC_OBJECT_NAME} PRIVATE $<$<COMPILE_LANGUAGE:C>:${KMCMAKE_CC_OBJECT_COPTS}>)
    target_compile_options(${KMCMAKE_CC_OBJECT_NAME} PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${KMCMAKE_CC_OBJECT_CXXOPTS}>)
    target_compile_options(${KMCMAKE_CC_OBJECT_NAME} PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:${KMCMAKE_CC_OBJECT_CUOPTS}>)
    target_include_directories(${KMCMAKE_CC_OBJECT_NAME} ${${KMCMAKE_CC_OBJECT_NAME}_INCLUDE_SYSTEM}
            PUBLIC
            ${KMCMAKE_CC_OBJECT_INCLUDES}
            "$<BUILD_INTERFACE:${${PROJECT_NAME}_SOURCE_DIR}>"
            "$<BUILD_INTERFACE:${${PROJECT_NAME}_BINARY_DIR}>"
            "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
    )
    target_include_directories(${KMCMAKE_CC_OBJECT_NAME} ${${KMCMAKE_CC_OBJECT_NAME}_INCLUDE_SYSTEM}
            PRIVATE
            ${KMCMAKE_CC_OBJECT_PINCLUDES}
    )

    target_compile_definitions(${KMCMAKE_CC_OBJECT_NAME}
            PUBLIC
            ${KMCMAKE_CC_OBJECT_DEFINES}
    )

    foreach (arg IN LISTS KMCMAKE_CC_OBJECT_UNPARSED_ARGUMENTS)
        message(WARNING "Unparsed argument: ${arg}")
    endforeach ()

endfunction()

################################################################################################
# kmcmake_cc_test
################################################################################################

function(kmcmake_cc_test)
    set(options
            DISABLED
            EXT
            EXCLUDE_SYSTEM
    )
    set(args NAME
            MODULE
    )
    set(list_args
            DEPS
            SOURCES
            DEFINES
            COPTS
            CXXOPTS
            CUOPTS
            INCLUDES
            COMMAND
            LINKS
    )

    cmake_parse_arguments(
            KMCMAKE_CC_TEST
            "${options}"
            "${args}"
            "${list_args}"
            ${ARGN}
    )
    if (NOT KMCMAKE_CC_TEST_MODULE)
        kmcmake_error("no module name")
    endif ()
    kmcmake_raw("-----------------------------------")
    kmcmake_print_label("Building Test" "${KMCMAKE_CC_TEST_NAME}")
    kmcmake_raw("-----------------------------------")

    set(${KMCMAKE_CC_TEST_NAME}_INCLUDE_SYSTEM SYSTEM)
    if (KMCMAKE_CC_LIB_EXCLUDE_SYSTEM)
        set(${KMCMAKE_CC_TEST_NAME}_INCLUDE_SYSTEM "")
    endif ()

    if (VERBOSE_KMCMAKE_BUILD)
        kmcmake_print_list_label("Sources" KMCMAKE_CC_TEST_SOURCES)
        kmcmake_print_list_label("Deps" KMCMAKE_CC_TEST_DEPS)
        kmcmake_print_list_label("COPTS" KMCMAKE_CC_TEST_COPTS)
        kmcmake_print_list_label("Defines" KMCMAKE_CC_TEST_DEFINES)
        kmcmake_print_list_label("Links" KMCMAKE_CC_TEST_LINKS)
        message("-----------------------------------")
    endif ()
    set(KMCMAKE_RUN_THIS_TEST ON)
    if (KMCMAKE_CC_TEST_SKIP)
        set(KMCMAKE_RUN_THIS_TEST OFF)
    endif ()
    if (KMCMAKE_CC_TEST_EXT)
        set(KMCMAKE_RUN_THIS_TEST OFF)
    endif ()

    set(testcase ${KMCMAKE_CC_TEST_MODULE}_${KMCMAKE_CC_TEST_NAME})
    if (${KMCMAKE_CC_TEST_MODULE} IN_LIST ${PROJECT_NAME}_SKIP_TEST)
        set(KMCMAKE_RUN_THIS_TEST OFF)
    endif ()

    add_executable(${testcase} ${KMCMAKE_CC_TEST_SOURCES})

    target_compile_options(${testcase} PRIVATE $<$<COMPILE_LANGUAGE:C>:${KMCMAKE_CC_TEST_COPTS}>)
    target_compile_options(${testcase} PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${KMCMAKE_CC_TEST_CXXOPTS}>)
    target_compile_options(${testcase} PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:${KMCMAKE_CC_TEST_CUOPTS}>)
    if (KMCMAKE_CC_TEST_DEPS)
        add_dependencies(${testcase} ${KMCMAKE_CC_TEST_DEPS})
    endif ()
    target_link_libraries(${testcase} PRIVATE ${KMCMAKE_CC_TEST_LINKS})

    target_compile_definitions(${testcase}
            PUBLIC
            ${KMCMAKE_CC_TEST_DEFINES}
    )

    target_include_directories(${testcase} ${${KMCMAKE_CC_TEST_NAME}_INCLUDE_SYSTEM}
            PUBLIC
            ${KMCMAKE_CC_TEST_INCLUDES}
            "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>"
            "$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}>"
            "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
    )
    if (NOT KMCMAKE_CC_TEST_COMMAND)
        set(KMCMAKE_CC_TEST_COMMAND ${testcase})
    endif ()

    if (KMCMAKE_RUN_THIS_TEST)
        add_test(NAME ${testcase}
                COMMAND ${KMCMAKE_CC_TEST_COMMAND})
    endif ()

endfunction()

function(kmcmake_cc_test_ext)
    set(options
            DISABLE
    )
    set(args NAME
            MODULE
            ALIAS
    )
    set(list_args
            ARGS
            FAIL_EXP
            SKIP_EXP
            PASS_EXP
    )

    cmake_parse_arguments(
            KMCMAKE_CC_TEST_EXT
            "${options}"
            "${args}"
            "${list_args}"
            ${ARGN}
    )

    set(KMCMAKE_RUN_THIS_TEST ON)
    if (KMCMAKE_CC_TEST_EXT_DISABLE)
        set(KMCMAKE_RUN_THIS_TEST OFF)
    endif ()

    if (KMCMAKE_CC_TEST_EXT_MODULE)
        set(basecmd ${KMCMAKE_CC_TEST_EXT_MODULE}_${KMCMAKE_CC_TEST_EXT_NAME})
        if (${KMCMAKE_CC_TEST_EXT_MODULE} IN_LIST ${PROJECT_NAME}_SKIP_TEST)
            set(KMCMAKE_RUN_THIS_TEST OFF)
        endif ()
    else ()
        set(basecmd ${KMCMAKE_CC_TEST_EXT_NAME})
    endif ()

    if (KMCMAKE_CC_TEST_EXT_ALIAS)
        set(test_name ${KMCMAKE_CC_TEST_EXT_MODULE}_${KMCMAKE_CC_TEST_EXT_NAME}_${KMCMAKE_CC_TEST_EXT_ALIAS})
    else ()
        set(test_name ${KMCMAKE_CC_TEST_EXT_MODULE}_${KMCMAKE_CC_TEST_EXT_NAME})
    endif ()

    if (KMCMAKE_RUN_THIS_TEST)
        add_test(NAME ${test_name} COMMAND ${basecmd} ${KMCMAKE_CC_TEST_EXT_ARGS})
        if (KMCMAKE_CC_TEST_EXT_FAIL_EXP)
            set_property(TEST ${test_name} PROPERTY FAIL_REGULAR_EXPRESSION ${KMCMAKE_CC_TEST_EXT_FAIL_EXP})
        endif ()
        if (KMCMAKE_CC_TEST_EXT_PASS_EXP)
            set_property(TEST ${test_name} PROPERTY PASS_REGULAR_EXPRESSION ${KMCMAKE_CC_TEST_EXT_PASS_EXP})
        endif ()
        if (KMCMAKE_CC_TEST_EXT_SKIP_EXP)
            set_property(TEST ${test_name} PROPERTY SKIP_REGULAR_EXPRESSION ${KMCMAKE_CC_TEST_EXT_SKIP_EXP})
        endif ()
    endif ()

endfunction()

################################################################################################
# kmcmake_cc_binary
################################################################################################

function(kmcmake_cc_binary)
    set(options
            PUBLIC
            EXCLUDE_SYSTEM
    )
    set(list_args
            DEPS
            SOURCES
            DEFINES
            COPTS
            CXXOPTS
            CUOPTS
            LINKS
            INCLUDES
    )

    cmake_parse_arguments(
            KMCMAKE_CC_BINARY
            "${options}"
            "NAME"
            "${list_args}"
            ${ARGN}
    )

    set(${KMCMAKE_CC_BINARY_NAME}_INCLUDE_SYSTEM SYSTEM)
    if (KMCMAKE_CC_LIB_EXCLUDE_SYSTEM)
        set(${KMCMAKE_CC_BINARY_NAME}_INCLUDE_SYSTEM "")
    endif ()

    kmcmake_raw("-----------------------------------")
    kmcmake_print_label("Building Binary" "${KMCMAKE_CC_BINARY_NAME}")
    kmcmake_raw("-----------------------------------")
    if (VERBOSE_KMCMAKE_BUILD)
        kmcmake_print_list_label("Sources" KMCMAKE_CC_BINARY_SOURCES)
        kmcmake_print_list_label("Deps" KMCMAKE_CC_BINARY_DEPS)
        kmcmake_print_list_label("COPTS" KMCMAKE_CC_BINARY_COPTS)
        kmcmake_print_list_label("CXXOPTS" KMCMAKE_CC_BINARY_CXXOPTS)
        kmcmake_print_list_label("CUOPTS" KMCMAKE_CC_BINARY_CUOPTS)
        kmcmake_print_list_label("Defines" KMCMAKE_CC_BINARY_DEFINES)
        kmcmake_print_list_label("Includes" KMCMAKE_CC_BINARY_INCLUDES)
        kmcmake_print_list_label("Links" KMCMAKE_CC_BINARY_LINKS)
        message("-----------------------------------")
    endif ()

    set(exec_case ${KMCMAKE_CC_BINARY_NAME})

    add_executable(${exec_case} ${KMCMAKE_CC_BINARY_SOURCES})

    target_compile_options(${exec_case} PRIVATE $<$<COMPILE_LANGUAGE:C>:${KMCMAKE_CC_BINARY_COPTS}>)
    target_compile_options(${exec_case} PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${KMCMAKE_CC_BINARY_CXXOPTS}>)
    target_compile_options(${exec_case} PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:${KMCMAKE_CC_BINARY_CUOPTS}>)
    if (KMCMAKE_CC_BINARY_DEPS)
        add_dependencies(${exec_case} ${KMCMAKE_CC_BINARY_DEPS})
    endif ()
    target_link_libraries(${exec_case} PRIVATE ${KMCMAKE_CC_BINARY_LINKS})

    target_compile_definitions(${exec_case}
            PUBLIC
            ${KMCMAKE_CC_BINARY_DEFINES}
    )

    target_include_directories(${exec_case} ${${KMCMAKE_CC_LIB_NAME}_INCLUDE_SYSTEM}
            PRIVATE
            ${KMCMAKE_CC_BINARY_INCLUDES}
            "$<BUILD_INTERFACE:${${PROJECT_NAME}_SOURCE_DIR}>"
            "$<BUILD_INTERFACE:${${PROJECT_NAME}_BINARY_DIR}>"
            "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
    )
    if (KMCMAKE_CC_BINARY_PUBLIC)
        install(TARGETS ${exec_case}
                EXPORT ${PROJECT_NAME}Targets
                RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
                LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
                ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
                INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
        )
    endif (KMCMAKE_CC_BINARY_PUBLIC)

endfunction()

################################################################################################
# kmcmake_cc_benchmark
################################################################################################

function(kmcmake_cc_bm)
    set(options
            DISABLED
            EXT
    )
    set(args NAME
            MODULE
    )
    set(list_args
            DEPS
            SOURCES
            DEFINES
            COPTS
            CXXOPTS
            CUOPTS
            INCLUDES
            COMMAND
            LINKS
    )

    cmake_parse_arguments(
            KMCMAKE_CC_BM
            "${options}"
            "${args}"
            "${list_args}"
            ${ARGN}
    )

    if (NOT KMCMAKE_CC_BM_MODULE)
        kmcmake_error("no module name to the bm")
    endif ()

    kmcmake_raw("-----------------------------------")
    kmcmake_print_label("Building Benchmark" "${KMCMAKE_CC_BM_NAME}")
    kmcmake_raw("-----------------------------------")
    if (VERBOSE_KMCMAKE_BUILD)
        kmcmake_print_list_label("Sources" KMCMAKE_CC_BM_SOURCES)
        kmcmake_print_list_label("Deps" KMCMAKE_CC_BM_DEPS)
        kmcmake_print_list_label("COPTS" KMCMAKE_CC_BM_COPTS)
        kmcmake_print_list_label("Defines" KMCMAKE_CC_BM_DEFINES)
        kmcmake_print_list_label("Liniks" KMCMAKE_CC_BM_LINKS)
        message("-----------------------------------")
    endif ()
    set(${KMCMAKE_CC_BM_NAME}_INCLUDE_SYSTEM SYSTEM)
    if (KMCMAKE_CC_LIB_EXCLUDE_SYSTEM)
        set(${KMCMAKE_CC_BM_NAME}_INCLUDE_SYSTEM "")
    endif ()

    set(KMCMAKE_RUN_THIS_TEST ON)
    if (KMCMAKE_CC_BM_SKIP)
        set(KMCMAKE_RUN_THIS_TEST OFF)
    endif ()
    if (KMCMAKE_CC_BM_EXT)
        set(KMCMAKE_RUN_THIS_TEST OFF)
    endif ()
    set(testcase ${KMCMAKE_CC_BM_MODULE}_${KMCMAKE_CC_BM_NAME})
    if (${KMCMAKE_CC_BM_MODULE} IN_LIST ${PROJECT_NAME}_SKIP_BENCHMARK)
        set(KMCMAKE_RUN_THIS_TEST OFF)
    endif ()

    add_executable(${testcase} ${KMCMAKE_CC_BM_SOURCES})

    target_compile_options(${testcase} PRIVATE $<$<COMPILE_LANGUAGE:C>:${KMCMAKE_CC_BM_COPTS}>)
    target_compile_options(${testcase} PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${KMCMAKE_CC_BM_CXXOPTS}>)
    target_compile_options(${testcase} PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:${KMCMAKE_CC_BM_CUOPTS}>)
    if (KMCMAKE_CC_BM_DEPS)
        add_dependencies(${testcase} ${KMCMAKE_CC_BM_DEPS})
    endif ()
    target_link_libraries(${testcase} PRIVATE ${KMCMAKE_CC_BM_LINKS})
    target_compile_definitions(${testcase}
            PUBLIC
            ${KMCMAKE_CC_BM_DEFINES}
    )

    target_include_directories(${testcase} ${${KMCMAKE_CC_BM_NAME}_INCLUDE_SYSTEM}
            PUBLIC
            ${KMCMAKE_CC_BM_INCLUDES}
            "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>"
            "$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}>"
            "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
    )
    if (NOT KMCMAKE_CC_BM_COMMAND)
        set(KMCMAKE_CC_BM_COMMAND ${testcase})
    endif ()

    if (KMCMAKE_RUN_THIS_TEST)
        add_test(NAME ${testcase}
                COMMAND ${KMCMAKE_CC_BM_COMMAND})
    endif ()

endfunction()

function(kmcmake_cc_bm_ext)
    set(options
            DISABLE
    )
    set(args NAME
            MODULE
            ALIAS
    )
    set(list_args
            ARGS
            FAIL_EXP
            SKIP_EXP
            PASS_EXP
    )

    cmake_parse_arguments(
            KMCMAKE_CC_BM_EXT
            "${options}"
            "${args}"
            "${list_args}"
            ${ARGN}
    )

    set(KMCMAKE_RUN_THIS_TEST ON)
    if (KMCMAKE_CC_BM_EXT_DISABLE)
        set(KMCMAKE_RUN_THIS_TEST OFF)
    endif ()

    if (KMCMAKE_CC_BM_EXT_MODULE)
        set(basecmd ${KMCMAKE_CC_BM_EXT_MODULE}_${KMCMAKE_CC_BM_EXT_NAME})
        if (${KMCMAKE_CC_BM_EXT_MODULE} IN_LIST ${PROJECT_NAME}_SKIP_BENCHMARK)
            set(KMCMAKE_RUN_THIS_TEST OFF)
        endif ()
    else ()
        set(basecmd ${KMCMAKE_CC_BM_EXT_NAME})
    endif ()

    if (KMCMAKE_CC_BM_EXT_ALIAS)
        set(test_name ${KMCMAKE_CC_BM_EXT_MODULE}_${KMCMAKE_CC_BM_EXT_NAME}_${KMCMAKE_CC_BM_EXT_ALIAS})
    else ()
        set(test_name ${KMCMAKE_CC_BM_EXT_MODULE}_${KMCMAKE_CC_BM_EXT_NAME})
    endif ()

    if (KMCMAKE_RUN_THIS_TEST)
        add_test(NAME ${test_name} COMMAND ${basecmd} ${KMCMAKE_CC_BM_EXT_ARGS})
        if (KMCMAKE_CC_BM_EXT_FAIL_EXP)
            set_property(TEST ${test_name} PROPERTY FAIL_REGULAR_EXPRESSION ${KMCMAKE_CC_BM_EXT_FAIL_EXP})
        endif ()
        if (KMCMAKE_CC_BM_EXT_PASS_EXP)
            set_property(TEST ${test_name} PROPERTY PASS_REGULAR_EXPRESSION ${KMCMAKE_CC_BM_EXT_PASS_EXP})
        endif ()
        if (KMCMAKE_CC_BM_EXT_SKIP_EXP)
            set_property(TEST ${test_name} PROPERTY SKIP_REGULAR_EXPRESSION ${KMCMAKE_CC_BM_EXT_SKIP_EXP})
        endif ()
    endif ()

endfunction()

################################################################################################
# kmcmake_target_check
################################################################################################
function(kmcmake_target_check my_target)
    if (NOT TARGET ${my_target})
        message(FATAL_ERROR " KMCMAKE: compiling ${PROJECT_NAME} requires a ${my_target} CMake target in your project,
                   see CMake/README.md for more details")
    endif (NOT TARGET ${my_target})
endfunction()

################################################################################################
# kmcmake_simd
################################################################################################
INCLUDE(CheckCXXSourceRuns)

SET(SSE1_CODE "
  #include <xmmintrin.h>

  int main()
  {
    __m128 a;
    float vals[4] = {0,0,0,0};
    a = _mm_loadu_ps(vals);  // SSE1
    return 0;
  }")

SET(SSE2_CODE "
  #include <emmintrin.h>

  int main()
  {
    __m128d a;
    double vals[2] = {0,0};
    a = _mm_loadu_pd(vals);  // SSE2
    return 0;
  }")

SET(SSE3_CODE "
#include <pmmintrin.h>
int main() {
    __m128 u, v;
    u = _mm_set1_ps(0.0f);
    v = _mm_moveldup_ps(u); // SSE3
    return 0;
}")

SET(SSSE3_CODE "
  #include <tmmintrin.h>
  const double v = 0;
  int main() {
    __m128i a = _mm_setzero_si128();
    __m128i b = _mm_abs_epi32(a); // SSSE3
    return 0;
  }")

SET(SSE4_1_CODE "
  #include <smmintrin.h>

  int main ()
  {
    __m128i a = {0,0,0,0}, b = {0,0,0,0};
    __m128i res = _mm_max_epi8(a, b); // SSE4_1

    return 0;
  }
")

SET(SSE4_2_CODE "
  #include <nmmintrin.h>

  int main()
  {
    __m128i a = {0,0,0,0}, b = {0,0,0,0}, c = {0,0,0,0};
    c = _mm_cmpgt_epi64(a, b);  // SSE4_2
    return 0;
  }
")


MACRO(CHECK_SSE lang type flags)
    SET(__FLAG_I 1)
    SET(CMAKE_REQUIRED_FLAGS_SAVE ${CMAKE_REQUIRED_FLAGS})
    FOREACH (__FLAG ${flags})
        IF (NOT ${lang}_${type}_FOUND)
            SET(CMAKE_REQUIRED_FLAGS ${__FLAG})
            CHECK_CXX_SOURCE_RUNS("${${type}_CODE}" ${lang}_HAS_${type}_${__FLAG_I})
            IF (${lang}_HAS_${type}_${__FLAG_I})
                SET(${lang}_${type}_FOUND TRUE)
            ENDIF ()
            MATH(EXPR __FLAG_I "${__FLAG_I}+1")
        ENDIF ()
    ENDFOREACH ()
    SET(CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS_SAVE})

    IF (NOT ${lang}_${type}_FOUND)
        SET(${lang}_${type}_FOUND FALSE)
    ENDIF ()
    MARK_AS_ADVANCED(${lang}_${type}_FOUND ${lang}_${type}_FLAGS)

ENDMACRO()

SET(AVX_CODE "
#if !defined __AVX__ // MSVC supports this flag since MSVS 2013
#error \"__AVX__ define is missing\"
#endif
#include <immintrin.h>
void test()
{
    __m256 a = _mm256_set1_ps(0.0f);
}
int main() { return 0; }")

SET(AVX2_CODE "
#if !defined __AVX2__ // MSVC supports this flag since MSVS 2013
#error \"__AVX2__ define is missing\"
#endif
#include <immintrin.h>
void test()
{
    int data[8] = {0,0,0,0, 0,0,0,0};
    __m256i a = _mm256_loadu_si256((const __m256i *)data);
    __m256i b = _mm256_bslli_epi128(a, 1);  // available in GCC 4.9.3+
}
int main() { return 0; }")

SET(AVX512_CODE "
#if defined __AVX512__ || defined __AVX512F__
#include <immintrin.h>
void test()
{
    __m512i zmm = _mm512_setzero_si512();
#if defined __GNUC__ && defined __x86_64__
    asm volatile (\"\" : : : \"zmm16\", \"zmm17\", \"zmm18\", \"zmm19\");
#endif
}
#else
#error \"AVX512 is not supported\"
#endif
int main() { return 0; }")


MACRO(CHECK_AVX type flags)
    SET(__FLAG_I 1)
    SET(CMAKE_REQUIRED_FLAGS_SAVE ${CMAKE_REQUIRED_FLAGS})
    FOREACH (__FLAG ${flags})
        IF (NOT CXX_${type}_FOUND)
            SET(CMAKE_REQUIRED_FLAGS ${__FLAG})
            CHECK_CXX_SOURCE_RUNS("${${type}_CODE}" CXX_HAS_${type}_${__FLAG_I})
            IF (CXX_HAS_${type}_${__FLAG_I})
                SET(CXX_${type}_FOUND TRUE)
            ENDIF ()
            MATH(EXPR __FLAG_I "${__FLAG_I}+1")
        ENDIF ()
    ENDFOREACH ()
    SET(CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS_SAVE})

    IF (NOT CXX_${type}_FOUND)
        SET(CXX_${type}_FOUND FALSE)
    ENDIF ()

    MARK_AS_ADVANCED(CXX_${type}_FOUND CXX_${type}_FLAGS)

ENDMACRO()
if ("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "AMD64")
    CHECK_AVX("AVX" ";-mavx;/arch:AVX")
    CHECK_AVX("AVX2" ";-mavx2;/arch:AVX2")
    CHECK_AVX("AVX512" ";-mavx512;/arch:AVX512")
    CHECK_SSE(CXX "SSE1" ";-msse;/arch:SSE")
    CHECK_SSE(CXX "SSE2" ";-msse2;/arch:SSE2")
    CHECK_SSE(CXX "SSE3" ";-msse3;/arch:SSE3")
    CHECK_SSE(CXX "SSSE3" ";-mssse3;/arch:SSSE3")
    CHECK_SSE(CXX "SSE4_1" ";-msse4.1;-msse4;/arch:SSE4")
    CHECK_SSE(CXX "SSE4_2" ";-msse4.2;-msse4;/arch:SSE4")
elseif ("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "arm.*|aarch64")
    IF (CMAKE_SYSTEM_NAME MATCHES "Linux")
        execute_process(COMMAND cat /proc/cpuinfo OUTPUT_VARIABLE CPUINFO)

        #neon instruction can be found on the majority part of modern ARM processor
        STRING(REGEX REPLACE "^.*(neon).*$" "\\1" NEON_THERE ${CPUINFO})
        STRING(COMPARE EQUAL "neon" "${NEON_THERE}" NEON_TRUE)
        IF (NEON_TRUE)
            set(NEON_FOUND true BOOL "NEON available on host")
        ELSE ()
            set(NEON_FOUND false BOOL "NEON available on host")
        ENDIF ()

        #Find the processor type (for now OMAP3 or OMAP4)
        STRING(REGEX REPLACE "^.*(OMAP3).*$" "\\1" OMAP3_THERE ${CPUINFO})
        STRING(COMPARE EQUAL "OMAP3" "${OMAP3_THERE}" OMAP3_TRUE)
        IF (OMAP3_TRUE)
            set(CORTEXA8_FOUND true BOOL "OMAP3 available on host")
        ELSE ()
            set(CORTEXA8_FOUND false BOOL "OMAP3 available on host")
        ENDIF ()

        #Find the processor type (for now OMAP3 or OMAP4)
        STRING(REGEX REPLACE "^.*(OMAP4).*$" "\\1" OMAP4_THERE ${CPUINFO})
        STRING(COMPARE EQUAL "OMAP4" "${OMAP4_THERE}" OMAP4_TRUE)
        IF (OMAP4_TRUE)
            set(CORTEXA9_FOUND true BOOL "OMAP4 available on host")
        ELSE ()
            set(CORTEXA9_FOUND false BOOL "OMAP4 available on host")
        ENDIF ()

    ELSEIF (CMAKE_SYSTEM_NAME MATCHES "Darwin")
        execute_process(COMMAND /usr/sbin/sysctl -n machdep.cpu.features OUTPUT_VARIABLE CPUINFO)

        #neon instruction can be found on the majority part of modern ARM processor
        STRING(REGEX REPLACE "^.*(neon).*$" "\\1" NEON_THERE ${CPUINFO})
        STRING(COMPARE EQUAL "neon" "${NEON_THERE}" NEON_TRUE)
        IF (NEON_TRUE)
            set(NEON_FOUND true BOOL "NEON available on host")
        ELSE ()
            set(NEON_FOUND false BOOL "NEON available on host")
        ENDIF ()

    ELSEIF (CMAKE_SYSTEM_NAME MATCHES "Windows")
        # TODO
        set(CORTEXA8_FOUND false BOOL "OMAP3 not available on host")
        set(CORTEXA9_FOUND false BOOL "OMAP4 not available on host")
        set(NEON_FOUND false BOOL "NEON not available on host")
    ELSE (CMAKE_SYSTEM_NAME MATCHES "Linux")
        set(CORTEXA8_FOUND false BOOL "OMAP3 not available on host")
        set(CORTEXA9_FOUND false BOOL "OMAP4 not available on host")
        set(NEON_FOUND false BOOL "NEON not available on host")
    ENDIF ()

    if (NOT NEON_FOUND)
        MESSAGE(STATUS "Could not find hardware support for NEON on this machine.")
    endif ()
    if (NOT CORTEXA8_FOUND)
        MESSAGE(STATUS "No OMAP3 processor on this on this machine.")
    endif ()
    if (NOT CORTEXA9_FOUND)
        MESSAGE(STATUS "No OMAP4 processor on this on this machine.")
    endif ()
    mark_as_advanced(NEON_FOUND)
endif ()
################################################################################################
# out of source build
################################################################################################
macro(KMCMAKE_ENSURE_OUT_OF_SOURCE_BUILD errorMessage)

    string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}" is_insource)
    if (is_insource)
        kmcmake_error(${errorMessage} "In-source builds are not allowed.
    CMake would overwrite the makefiles distributed with Compiler-RT.
    Please create a directory and run cmake from there, passing the path
    to this source directory as the last argument.
    This process created the file `CMakeCache.txt' and the directory `CMakeFiles'.
    Please delete them.")

    endif (is_insource)

endmacro(KMCMAKE_ENSURE_OUT_OF_SOURCE_BUILD)

option(KMCMAKE_USE_SYSTEM_INCLUDES "" OFF)
if (VERBOSE_CMAKE_BUILD)
    set(CMAKE_VERBOSE_MAKEFILE ON)
endif ()

if (KMCMAKE_USE_CXX11_ABI)
    add_definitions(-D_GLIBCXX_USE_CXX11_ABI=1)
elseif ()
    add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0)
endif ()

if (CONDA_ENV_ENABLE)
    list(APPEND CMAKE_PREFIX_PATH $ENV{CONDA_PREFIX})
    include_directories($ENV{CONDA_PREFIX}/include)
    link_directories($ENV{CONDA_PREFIX}/${CMAKE_INSTALL_LIBDIR})
endif ()

if (KMCMAKE_INSTALL_LIB)
    set(CMAKE_INSTALL_LIBDIR lib)
endif ()

if (KMCMAKE_USE_SYSTEM_INCLUDES)
    set(KMCMAKE_INTERNAL_INCLUDE_WARNING_GUARD SYSTEM)
else ()
    set(KMCMAKE_INTERNAL_INCLUDE_WARNING_GUARD "")
endif ()

KMCMAKE_ENSURE_OUT_OF_SOURCE_BUILD("must out of source dir")

#if (NOT DEV_MODE AND ${PROJECT_VERSION} MATCHES "0.0.0")
#    kmcmake_error("PROJECT_VERSION must be set in file project_profile or set -DDEV_MODE=true for develop debug")
#endif()

function(kmcmake_cc_proto OUT_HDRS OUT_SRCS DESTDIR HDR_OUTPUT_DIR PROTO_DIR PROTO_FILES)
    foreach (P ${PROTO_FILES})
        string(REPLACE .proto .pb.h HDR ${P})
        set(HDR_RELATIVE ${HDR})
        set(HDR ${DESTDIR}/${HDR})
        string(REPLACE .proto .pb.cc SRC ${P})
        set(SRC ${DESTDIR}/${SRC})
        list(APPEND HDRS ${HDR})
        list(APPEND SRCS ${SRC})
        add_custom_command(
                OUTPUT ${HDR} ${SRC}
                COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} ${PROTOC_FLAGS} -I${PROTO_DIR} --cpp_out=${DESTDIR} ${PROTO_DIR}/${P}
                COMMAND ${CMAKE_COMMAND} -E copy ${HDR} ${HDR_OUTPUT_DIR}/${HDR_RELATIVE}
                COMMAND ${CMAKE_COMMAND} -E copy ${HDR} ${PROTO_DIR}/${HDR_RELATIVE}
                DEPENDS ${PROTO_DIR}/${P}
        )
    endforeach ()
    set(${OUT_HDRS} ${HDRS} PARENT_SCOPE)
    set(${OUT_SRCS} ${SRCS} PARENT_SCOPE)
endfunction()
